JS DOM 渲染流程,以及重排和重绘
要点速览
shell
# DOM渲染流程
浏览器解析 HTML 生成 DOM 树,解析 CSS 生成 CSSOM 树,两者合并为渲染树,然后进行 ** 布局(重排 Reflow)计算元素大小位置,再进行绘制(重绘 Repaint)** 填充样式,最后分层合成显示页面。
# 重排 & 重绘
重排:计算元素布局尺寸位置,开销极大
重绘:绘制颜色样式,开销较小
重排必触发重绘,重绘不一定重排
# DOM 性能优化
1. 减少 DOM 操作:使用文档碎片 DocumentFragment,批量插入,避免循环频繁操作 DOM
2. 优化重排:读写分离、统一修改类名而非行内样式、优先 transform 动画、脱离文档流
3. 优化重绘:批量修改样式,避免频繁修改外观样式
4. 动画使用 requestAnimationFrame:与浏览器渲染帧同步,避免掉帧卡顿,减少无效重排重绘拓展
shell
# DOM渲染流程
解析HTML→生成DOM树→解析CSS→生成CSSOM树→合成渲染树(Render Tree)→布局(Layout)→绘制(Paint)
# 重排 Reflow(回流 / 布局)
计算元素几何信息:宽、高、位置、尺寸、布局结构发生变化会触发重排
# 重绘 Repaint(绘制)
只改变样式外观,不改变布局大小位置颜色、背景色、阴影、边框颜色、文字颜色等。开销比重排小,但频繁触发依然卡顿。
# 重排和重绘关系
重排一定触发重绘、重绘不一定触发重排
# 注意
合成属性(transform、opacity)不触发重排重绘,走 GPU 合成,性能最好
# 触发重排的操作
- 获取 / 修改宽高、top、left、width、height、offset 系列
- 新增 / 删除 DOM 节点
- 改变元素布局(display、position、float)
- 窗口大小改变
# 触发重绘的操作
color、background、box-shadow、outline 等
# 为什么频繁 DOM 操作会卡?
每一次修改 DOM 样式 / 结构,浏览器都会立刻重排重绘
多次零散 DOM 操作 → 多次重排 → 页面抖动、卡顿、掉帧
# 如何减少重排和重绘
先隐藏再操作,操作完再显示:display:none 脱离渲染树,修改不触发重排改完再恢复显示,只触发一次重排。
避免循环中频繁 appendChild:循环不要一次次插入 DOM,先内存构建,最后一次性插入。
不要频繁读取 DOM 布局属性:offsetWidth、clientHeight、scrollTop 等浏览器会强制同步刷新布局(强制同步布局),非常耗性能。原则:先全部读,再全部写,不要读写交替。
统一修改样式(修改 class ),不要多次改 style
动画、移动效果优先 transform,不用 top/left
总结:批量操作代替单个频繁操作,布局属性不要读写交替,而是先全部读缓存下来再写
# requestAnimationFrame 优化
浏览器每一帧渲染结束后执行的 API,跟浏览器刷新率同步(60 帧≈16.7ms)
解决什么问题?
自己写 setTimeout/setInterval 动画:掉帧、抖动、卡顿、提前渲染、浪费性能
会导致 JS 执行和浏览器渲染不同步,造成重排重绘混乱
所以
动画操作优先使用 requestAnimationFrame,与浏览器渲染帧同步,避免频繁无效重排重绘,提升流畅度。